/*

 ARCHIVO: "cmdline.h"

 Procesamiento de opciones en la línea de comando.
 Esta versión provee automáticamente "--help" y "--version".
 No admite parámetros no opción.

 Primera versión: 1-4-2012

*/


#ifndef _CMDLINE_H_INCLUDED_
#define _CMDLINE_H_INCLUDED_

#include <string>
#include <iostream>
#include <set>
#include <queue>
#include <list>

// Cola de parémetros.
typedef std::queue<std::string> Parameters_Queue_t;

// Vector de argumentos.
typedef std::vector<std::string> Arguments_Vector_t;

// Puntero a función de procesamiento de argumentos
// de opciones.
typedef void (*p_parse_argument_function_t)(Parameters_Queue_t &);

// Puntero a función de procesamiento de argumentos
// que no son opciones.
typedef void (*p_parse_non_option_function_t)(const std::string &);



/* ========================= ProgramVersion - Begin ========================== */
// Tipo para versión del programa.

struct ProgramVersion
{
    unsigned int major_;
    unsigned int minor_;
    unsigned int build_;

    // Operador de salida por stream. [OPERADOR]
    friend std::ostream& operator<<(std::ostream &os, const ProgramVersion &vr);
};

/* ========================= ProgramVersion - End ========================== */


/* ========================= CmdLine - Begin ========================== */
// Clase para parseo de la línea de comandos.


class CmdLine
{
private:

    /* ----------------- Tipos usados internamente - Begin ----------------- */


    /* ========================= CmdOption - Begin ========================== */
    // Opción de línea de comando.

    class CmdOption
    {
    public:

        // Cantidad de argumentos
        // que necesita la opción.
        unsigned int arg_count_;

        // Nombre corto de la opción.
        std::string short_name_;

        // Nombre largo de la opción.
        std::string long_name_;

        // Vector con argumentos
        // por omisión para la opción.
        Arguments_Vector_t def_values_;

        // Texto de ayuda.
        std::string help_text_;

        // Puntero a función de parseo.
        p_parse_argument_function_t parse_;

        // Indica si la opción puede
        // ser pasada mas de una vez.
        bool allow_repeat_;

        // Indica que la opción ya fué
        // procesada.
        bool ready_;

        // Constructor de opción completa.
        CmdOption(unsigned int argcount, const std::string &shortname,
                  const std::string &longname, const Arguments_Vector_t &defvalues,
                  const std::string &helptext, p_parse_argument_function_t fparse,
                  bool allowrepeat);

        // Constructor de opción completa sin o con un solo argumento.
        CmdOption(unsigned int argcount, const std::string &shortname,
                  const std::string &longname, const std::string &defvalue,
                  const std::string &helptext, p_parse_argument_function_t fparse,
                  bool allowrepeat);

        // Constructor de opción usado en la búsqueda.
        CmdOption(const std::string &shortname, const std::string &longname);

        // Destructor.
        ~CmdOption();

        // Lle na una cola con los parámetros por omisión de la opción.
        void Fill_Default_Args(Parameters_Queue_t &pars) const;

        // Devuelve un string con
        // todos los argumentos
        // por omisión.
        const std::string Str_Default_Args() const;

        // Devuelve el nombre de la opción.
        const std::string Option() const;
    };

    /* ========================= CmdOption - End ========================== */

    // Puntero a una opción.
    typedef CmdOption* PCmdOption;

    /* ========================= CompShortOption - Begin ========================== */
    // Clase función para comparación de Opciones para el std::set.

    struct CompShortOption
    {
        bool operator() (const PCmdOption& lopt, const PCmdOption& ropt) const;
    };

    /* ========================= CompShortOption - End ========================== */


    /* ========================= CompShortOption - Begin ========================== */
    // Clase función para comparación de Opciones para el std::set.

    struct CompLongOption
    {
        bool operator() (const PCmdOption& lopt, const PCmdOption& ropt) const;
    };

    /* ========================= CompShortOption - End ========================== */


    // Definiciones de tipos usados para abreviar declaraciones.

    // Lista de opciones.
    typedef std::list<CmdOption> Options_list_t;

    // Conjunto de punteros a opciones ordenadas por la opción corta.
    typedef std::set<PCmdOption, CompShortOption> POptions_so_set_t;

    // Conjunto de punteros a opciones ordenadas por la opción larga.
    typedef std::set<PCmdOption, CompLongOption> POptions_lo_set_t;

    // Par Iterador de opcion\booleano usado en las inserciones y búsquedas.
    typedef std::pair<POptions_so_set_t::const_iterator, bool> POptions_so_set_pair_t;

    // Par Iterador de opcion\booleano usado en las inserciones y búsquedas.
    typedef std::pair<POptions_lo_set_t::const_iterator, bool> POptions_lo_set_pair_t;

    /* ----------------- Tipos usados internamente - End ----------------- */


    /* Atributos - Begin */

    // Indica que ya se llamó a Parse
    bool parsed_;

    // Conjunto de las opciones especificadas.
    Options_list_t options_;

    // Set de punteros a opciones ordenados
    // por la opción corta.
    POptions_so_set_t set_so_poptions_;

    // Set de punteros a opciones ordenados
    // por la opción larga.
    POptions_lo_set_t set_lo_poptions_;

    // Este atributo contiene el nombre que se dio
    // al programa desde el constructor.
    std::string program_name_;

    // Este atributo contiene la descripción que se dio
    // del programa desde el constructor.
    std::string program_description_;

    // Este atributo contiene los parámetros por default para
    // el programa (se usa en el help).
    //std::string program_default_opts_;

    // Este atributo contiene la version del programa.
    ProgramVersion program_version_;

    // Este atributo contiene un puntero a la
    // función utilizada para procesar los
    // parámetros que no son opciones.
    // En caso de ser NULL indica que los mismos
    // no son válidos.
    p_parse_non_option_function_t parse_non_option_arg_;

    // Este atributo retiene el texto de la ayuda
    // generado dinamicamente.
    std::string help_text_;

    /* Atributos - End */

    // El constructor por defecto CmdLine::CmdLine(), es
    // privado, para evitar construir parsers sin parámetros.
    CmdLine();

    // Procesa una opción consumiendo parámetros de la cola.
    void Do_Option(const std::string &opt, Parameters_Queue_t &args, bool longopt);

    // Método que muestra el help del programa.
    void Do_Usage() const;

    // Método que muestra la versión del programa.
    void Do_Version() const;

    // Valida un nombre de opción.
    bool Validate_Name(const std::string& name);

public:

    // Constructor que toma el nombre, la descripción, la versión del programa
    // y un puntero a la función que procesa los argumentos que no son opciones.
    CmdLine(const std::string progname,
            const std::string progdescription, const ProgramVersion &progversion,
            p_parse_non_option_function_t parsenonoptionarg);

    // Constructor copia.
    CmdLine(const CmdLine &cmdr);

    // Destructor.
    ~CmdLine();

    // Método que agrega una nueva opción.
    // Retorna 0 en caso de que la opción
    // se haya agregado correctamente y un
    // código de error mayor o igual a
    // 1 en caso contrario.
    // Los parámetros son:
    // Cantidad de argumentos, el nombre corto,
    // el nombre largo, los valores por omisión,
    // el texto descriptivo, la función de parseo y
    // si se permite que la opción se repita.
    unsigned int Add_Option(unsigned int argcount, std::string short_name,
                            std::string long_name, const Arguments_Vector_t &def_values,
                            std::string help_text, p_parse_argument_function_t parse,
                            bool allow_repeat);

    // Método que agrega una nueva opción.
    // Retorna 0 en caso de que la opción
    // se haya agregado correctamente y un
    // código de error mayor o igual a
    // 1 en caso contrario.
    // Los parámetros son:
    // El nombre corto,
    // el nombre largo, el valor por omisión,
    // el texto descriptivo, la función de parseo y
    // si se permite que la opción se repita.
    unsigned int Add_Option(unsigned int argcount, std::string short_name,
                            std::string long_name, std::string def_value,
                            std::string help_text, p_parse_argument_function_t parse,
                            bool allow_repeat);

    // Método que parsea la línea de comandos.
    void Parse(int argc, char * const argv[]);

    // Operador asignación.
    CmdLine& operator=(const CmdLine &cmdr);
};

/* ========================= CmdLine - End ========================== */

#endif
